﻿using Microsoft.VisualStudio.Shell.Interop;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SharpGL.Enumerations;
using SolidEdgeAssembly;
using SolidEdgeConstants;
using SolidEdgeFramework;
using SolidEdgePart;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Windows.Shapes;
using TGComacAddin;
using TGComacAddin.command;
using TGComacAddin.res;
using TGComacAddin.View;
using TGCommunity;
using TGCommunity.AddIn;
using static TGComacAddin.TGComacAddin;


namespace TGComacAddin
{
    /// <summary>
    /// 用于存储全局常量
    /// </summary>
    public class TGC_Constants
    {
        public static double MM_TO_COORD = 0.001;
        public static  SolidEdgeFramework.Application SEAPP;
    }



    /// <summary>
    /// 天工CAD开发模板示例
    /// </summary>
    [ComVisible(true)]
    [Guid("8D43FCD5-0A5E-4286-9FA5-740FDDCDD4FE")]
    [ProgId("TGComacAddin")]
    public class TGComacAddin :
        TGCommunity.AddIn.TGAddIn, // TG CAD Community provided base class.       
        SolidEdgeFramework.ISEApplicationEvents, // Solid Edge Application Events
        SolidEdgeFramework.ISEAddInEdgeBarEventsEx,
        SolidEdgeFramework.ISEDocumentEvents
    {
        #region 成员变量
        /// <summary>
        /// 连接点入口
        /// </summary>
        public static TGCommunity.ConnectionPointController _connectionPointController { get; set; } = null;

        /// <summary>
        /// 选择事件连接点
        /// </summary>
        private TGCommunity.ConnectionPointController _selectSetChangeController { get; set; } = null;
        /// <summary>
        /// Ribbon容器
        /// </summary>
        private TGCommunity.AddIn.RibbonController _refRibbonController { get; set; } = null;
        /// <summary>
        /// 侧边栏容器
        /// </summary>
        private TGCommunity.AddIn.EdgeBarController _refEdgeBarController { get; set; } = null;
        /// <summary>
        /// 应用程序对象
        /// </summary>
        private SolidEdgeFramework.Application _application  = null;
        private SolidEdgeDocument _doc = null;

        public System.Windows.Interop.HwndSource _hwndSource1 { get; set; }
        #endregion 成员变量

        #region TGCommunity.AddIn.SolidEdgeAddIn overrides
        /// <summary>
        /// Called when the addin is first loaded by Solid Edge.
        /// </summary>
        /// 
        static string IntPtrToUtf8String(IntPtr ptr,int strSize)
        {
            // 获取指针指向的内存中的字节数组
            byte[] buffer = new byte[strSize]; // 假设最大长度为 1024 字节
            Marshal.Copy(ptr, buffer, 0, buffer.Length);

            // 将字节数组转换为字符串
            string result = Encoding.UTF8.GetString(buffer, 0, buffer.Length);

            return result;
        }


        [StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
        }

        enum MessageType : int
        {
            Select = 0, // 用于筛选
            GetMousePoint,
            UpdateTree,  // 用于更新树
        };

        /// <summary>
        /// 单选、多选消息处理
        /// </summary>
        /// <param name="port"></param>
        /// <param name="msgStr"></param>
        private void SelectProc(UInt16 port,string msgStr)
        {
            SelFilterArg arg = Newtonsoft.Json.JsonConvert.DeserializeObject<SelFilterArg>(msgStr);
            SolidEdgeFramework.Application SEAPP = _application;
            PartDocument partDoc = _application.GetActiveDocument() as PartDocument;

            SEAPP.AbortCommand(true);
            Command cmd = SEAPP.CreateCommand(SolidEdgeConstants.seCmdFlag.seNoDeactivate);
            SolidEdgeFramework.ISEMouseEx mouse = cmd.Mouse as SolidEdgeFramework.ISEMouseEx;

            // 查询结果
            string promptStr = arg.promptStr;
            bool isSingleSelect = arg.isSingleSelect;

            command.TGSelect selSingleCmd = new command.TGSelect(cmd.Mouse, cmd, partDoc, promptStr, arg.type, isSingleSelect);
            cmd.Start();

            Task.Run(() => {
                while (selSingleCmd.Status == SelectStatus.Running)
                {
                    System.Threading.Thread.Sleep(100);
                }

                List<UInt64> ids  = selSingleCmd.ElementIds.Select(x => x.Id).ToList();

                ResData rtData = new ResData { ErrorCode = (UInt32)selSingleCmd.Status, ElementIds = ids };
                string rtStr = JsonConvert.SerializeObject(rtData);
                try
                {
                    using (TcpClient tcpClient = new TcpClient())
                    {
                        tcpClient.Connect("localhost", port);
                        using (NetworkStream stream = tcpClient.GetStream())
                        {
                            byte[] buffer = Encoding.UTF8.GetBytes(rtStr);
                            stream.Write(buffer, 0, buffer.Length);
                            stream.Close();
                        }
                        tcpClient.Close();
                    }
                }
                catch (Exception ex)
                {
                    string exMsg = ex.Message;
                }
            });
        }



        /// <summary>
        /// 交互式获取指定面上某点处理
        /// </summary>
        /// <param name="port"></param>
        /// <param name="msgStr"></param>
        private void GetMousePointProc(UInt16 port, string msgStr)
        {
            GetMousePointArg arg = Newtonsoft.Json.JsonConvert.DeserializeObject<GetMousePointArg>(msgStr);
            SolidEdgeFramework.Application SEAPP = _application;
            PartDocument partDoc = _application.GetActiveDocument() as PartDocument;

            SEAPP.AbortCommand(true);
            Command cmd = SEAPP.CreateCommand(SolidEdgeConstants.seCmdFlag.seNoDeactivate);

            // 发起交互命令
            command.TGGetFacePoint cmdGetFacePoint = new TGGetFacePoint(cmd.Mouse, cmd, partDoc, arg.faceId,arg.pointNum);
            cmd.Start();

            // 查询结果并返回

            Task.Run(() =>
            {
                while (cmdGetFacePoint.Status == SelectStatus.Running)
                {
                    System.Threading.Thread.Sleep(100);
                }
                GetMousePointRes rtData = cmdGetFacePoint.Result;
                string rtStr = JsonConvert.SerializeObject(rtData);
                try
                {
                    using (TcpClient tcpClient = new TcpClient())
                    {
                        tcpClient.Connect("localhost", port);
                        using (NetworkStream stream = tcpClient.GetStream())
                        {
                            byte[] buffer = Encoding.UTF8.GetBytes(rtStr);
                            stream.Write(buffer, 0, buffer.Length);
                            stream.Close();
                        }
                        tcpClient.Close();
                    }
                }
                catch (Exception ex)
                {
                    string exMsg = ex.Message;
                }
            });
        }

        /// <summary>
        /// 拷贝数据消息处理，控制台控制窗口消息与此插件进行进程通信
        /// 在其中分发消息处理
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="msg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <param name="handled"></param>
        /// <returns></returns>
        private   IntPtr   CopyDataProc(IntPtr hwnd,int msg, IntPtr wParam, IntPtr lParam,ref bool handled)
        {
            // WM_COPYDATA
            handled = true;
            try
            {

                UInt16 port = (UInt16)wParam;
                COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));

                string msgStr = IntPtrToUtf8String(cds.lpData, cds.cbData);
                JObject jObj = JObject.Parse(msgStr);
                if (jObj.TryGetValue("msgType", out var msgTypeToken) && msgTypeToken.Type == JTokenType.Integer)
                {
                    MessageType msgType = (MessageType)msgTypeToken.ToObject<int>();
                    switch (msgType)
                    {
                        case MessageType.Select:
                            {
                                SelectProc(port,msgStr);
                            }
                            break;
                        case MessageType.GetMousePoint:
                            {
                                GetMousePointProc(port,msgStr);
                                break;
                            }
                        default:
                            {
                                return IntPtr.Zero;
                            }
                    }
                }
               
            }
            catch(Exception)
            {
                return IntPtr.Zero;
            }
            return (IntPtr)1;
        }

        /// <summary>
        /// 验证消息处理
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="msg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <param name="handled"></param>
        /// <returns></returns>
        private IntPtr CheckProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            handled = true;
            int a =(int) wParam;
            int b =(int) lParam;
            if(a!=11 || b!=10)
                return IntPtr.Zero;

            return (IntPtr)(a * b);
        }

        private int WM_CHECK = 1025;
        private int WM_UpdateTree = 1026;
        protected IntPtr MyWindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {

            if (msg == 0x4a)
            {
                return CopyDataProc(hwnd, msg, wParam, lParam, ref handled);
            }
            else if (msg == WM_CHECK)
            {
                return CheckProc(hwnd, msg, wParam, lParam, ref handled);
            }else if (msg == WM_UpdateTree)
            {
                TGEdgeBarView.S_MainView.UpdateTreeview();
            }

                return IntPtr.Zero;
        }


        public override void OnConnection(SolidEdgeFramework.Application application, SolidEdgeFramework.SeConnectMode ConnectMode, SolidEdgeFramework.AddIn AddInInstance)
        {
            Process currentProcess = Process.GetCurrentProcess();
            string exePath = currentProcess.MainModule.FileName;
            string exeName = System.IO.Path.GetFileName(exePath);
            if (string.Compare(exeName, "TianGong.exe", true) != 0)
                return;

            TGC_Constants.SEAPP = application;
            _application = application;
            // If you makes changes to your ribbon, be sure to increment the GuiVersion or your ribbon
            // will not initialize properly.
            AddInEx.GuiVersion = 2;

            // Create an instance of the default connection point controller. It helps manage connections to COM events.
            _connectionPointController = new TGCommunity.ConnectionPointController(this);

            // Uncomment the following line to attach to the Solid Edge Application Events.
            _connectionPointController.AdviseSink<SolidEdgeFramework.ISEApplicationEvents>(this.Application);
            _connectionPointController.AdviseSink<SolidEdgeFramework.ISEAddInEdgeBarEventsEx>(this.AddInInstance);
            //_applicationWindowEvents = _application.ApplicationWindowEvents as SolidEdgeFramework.ISEApplicationWindowEvents_Event;
            //_applicationWindowEvents.WindowProc += WindowProc;
            _selectSetChangeController = new TGCommunity.ConnectionPointController(this);
        }

        public struct SelFilterArg
        {
            public Int32 type { get; set; }
            public string promptStr { get; set; }
            public bool isSingleSelect { get; set; }
        }

        public struct ResData
        {
            public UInt32 ErrorCode { get; set; }
            public List<UInt64> ElementIds { get; set; }
        };

        public struct GetMousePointArg
        {
            public UInt64 faceId { get; set; }
            public Int64 pointNum { get; set; }
        }

        public struct GetMousePointRes
        {
            public UInt32 ErrorCode { get; set; }
            public double[] X;
            public double[] Y;
            public double[] Z;
        }

        static void SendToClient(in ResData resData)
        {

        }

        /// <summary>
        /// 选择调度线程
        /// </summary>

        /// <summary>
        /// 接收线程之线程函数
        /// </summary>


        /// <summary>
        /// Called when the addin first connects to a new Solid Edge environment.
        /// </summary>
        public override void OnConnectToEnvironment(SolidEdgeFramework.Environment environment, bool firstTime)
        {

        }

        /// <summary>
        /// Called when the addin is about to be unloaded by Solid Edge.
        /// </summary>
        public override void OnDisconnection(SolidEdgeFramework.SeDisconnectMode DisconnectMode)
        {
            // Disconnect from all COM events.
            _connectionPointController.UnadviseAllSinks();
        }

        /// <summary>
        /// Called when Solid Edge raises the SolidEdgeFramework.ISEAddInEdgeBarEvents[Ex].AddPage() event.
        /// </summary>
        public override void OnCreateEdgeBarPage(TGCommunity.AddIn.EdgeBarController controller, SolidEdgeFramework.SolidEdgeDocument document)
        {
            //_refEdgeBarController = controller;
           
        }

        /// <summary>
        /// Called directly after OnConnectToEnvironment() to give you an opportunity to configure a ribbon for a specific environment.
        /// </summary>
        public override void OnCreateRibbon(TGCommunity.AddIn.RibbonController controller, Guid environmentCategory, bool firstTime)
        {
            if (environmentCategory.Equals(TGSDK.EnvironmentCategories.Part) || environmentCategory.Equals(TGSDK.EnvironmentCategories.DMPart))
            {
                const string _embeddedResourceName = "TGComacAddin.TGRibbon.xml";
                controller.Add(environmentCategory, firstTime, new TGUIManager(_embeddedResourceName, System.Reflection.Assembly.GetExecutingAssembly()));
            }
        }
        #endregion

        #region SolidEdgeFramework.ISEApplicationEvents
        void ISEApplicationEvents.AfterActiveDocumentChange(object theDocument)
        {

            SolidEdgeFramework.SolidEdgeDocument doc = theDocument as SolidEdgeDocument;
            if (doc == null)
                return;
            if (_selectSetChangeController != null)
            {
                _selectSetChangeController.UnadviseAllSinks();
            }
            _selectSetChangeController.AdviseSink<SolidEdgeFramework.ISEDocumentEvents>(doc.DocumentEvents);
        }

        void ISEApplicationEvents.AfterCommandRun(int theCommandID)
        {

        }

        void ISEApplicationEvents.AfterDocumentOpen(object theDocument)
        {
            _doc = theDocument as SolidEdgeDocument;
        }

        void ISEApplicationEvents.AfterDocumentPrint(object theDocument, int hDC, ref double ModelToDC, ref int Rect)
        {

        }

        /// <summary>
        /// 用于查找获取重命名群文件的路径列表
        /// </summary>
        /// <param name="theDocument"></param>
        /// <param name="filePath"></param>
        public void GetReNameFiles(object theDocument,ref List<string> filePath)
        {
            if (theDocument is AssemblyDocument asmDoc == false) return;
            filePath.Clear();
            foreach (Occurrence occu in asmDoc.Occurrences)
            {
                filePath.Add(occu.OccurrenceFileName);
            }
        }

        void ISEApplicationEvents.AfterDocumentSave(object theDocument)
        {
            if (theDocument is SolidEdgeFramework.SolidEdgeDocument seDoc == false)
                return;
        
        }

        void ISEApplicationEvents.AfterEnvironmentActivate(object theEnvironment)
        {

        }

        void ISEApplicationEvents.AfterNewDocumentOpen(object theDocument)
        {

        }

        void ISEApplicationEvents.AfterNewWindow(object theWindow)
        {
           
        }

        void ISEApplicationEvents.AfterWindowActivate(object theWindow)
        {
          
        }

        void ISEApplicationEvents.BeforeCommandRun(int theCommandID)
        {
            //if(_doc != null)
            //{
            //    GetReNameFiles(_doc.Application.ActiveDocument, ref filePaths);
            //}
        }

        void ISEApplicationEvents.BeforeDocumentClose(object theDocument)
        {
            SolidEdgeFramework.SolidEdgeDocument seDoc = theDocument as SolidEdgeFramework.SolidEdgeDocument;
            string filePath = seDoc.FullName;
            fileEdgeBarIndexDic.Remove(filePath);
            docEdgeBarPageDic.Remove(seDoc);
        }

        void ISEApplicationEvents.BeforeDocumentPrint(object theDocument, int hDC, ref double ModelToDC, ref int Rect)
        {

        }

        void ISEApplicationEvents.BeforeEnvironmentDeactivate(object theEnvironment)
        {

        }

        void ISEApplicationEvents.BeforeWindowDeactivate(object theWindow)
        {

        }

        void ISEApplicationEvents.BeforeQuit()
        {

        }

        void ISEApplicationEvents.BeforeDocumentSave(object theDocument)
        {

        }
        #endregion SolidEdgeFramework.ISEApplicationEvents

        #region SolidEdgeFramework.ISEAddInEdgeBarEventsEx


        public HashSet<int> edgeBarIndexs = new HashSet<int>();
        Dictionary<SolidEdgeDocument, TGEdgeBarView> docEdgeBarPageDic = new Dictionary<SolidEdgeDocument, TGEdgeBarView>();
        Dictionary<string, TGEdgeBarView> fileEdgeBarPageDic = new Dictionary<string, TGEdgeBarView>();
        Dictionary<string, int> fileEdgeBarIndexDic = new Dictionary<string, int>();
        public int edgeBarIndex = 0;
        private TGEdgeBarView _tgMainPage = null;

        protected IntPtr ParentHookWinproc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            handled = true;
            return IntPtr.Zero;
        }

        void ISEAddInEdgeBarEventsEx.AddPage(object theDocument)
        {
            if (theDocument is SolidEdgeFramework.SolidEdgeDocument seDoc == false) return;

            EdgeBarPage edgeBarPage2 = null;
            string titleStr = "商飞铺层设计验证";

            if (docEdgeBarPageDic.Keys.Contains(seDoc))
            {
                _tgMainPage = docEdgeBarPageDic[seDoc];
                edgeBarPage2 = AddWpfEdgeBarFromNewPage(new EdgeBarPageConfiguration
                {
                    Caption = TGAddinResource.WPFEdgeBarCaption,
                    Index = 1,
                    NativeImageId = 107,
                    NativeResourcesDllPath = this.NativeResourcesDllPath,
                    Title = titleStr,
                    Tootip = TGAddinResource.WPFEdgeBarCaption
                }, _tgMainPage, document: seDoc);  
            }
            else
            {
                _tgMainPage = new TGEdgeBarView(seDoc);
                edgeBarPage2 = AddWpfEdgeBarFromNewPage(new EdgeBarPageConfiguration
                {
                    Caption = TGAddinResource.WPFEdgeBarCaption,
                    Index = 1,
                    NativeImageId = 107,
                    NativeResourcesDllPath = this.NativeResourcesDllPath,
                    Title = titleStr,
                    Tootip = TGAddinResource.WPFEdgeBarCaption
                }, _tgMainPage, document: seDoc);
                docEdgeBarPageDic.Add(seDoc, _tgMainPage);
            }

            if (edgeBarPage2 != null)
            {
                //  TGMainPage._winHandle = (int)edgeBarPage2.Handle;
                System.Windows.Interop.HwndSource hwndSource = (System.Windows.Interop.HwndSource)edgeBarPage2.ChildObject;
                if(hwndSource!=null)
                {
                    hwndSource.AddHook(MyWindowProc);
                    if (hwndSource != null)
                    {
                        _tgMainPage = hwndSource.RootVisual as TGEdgeBarView;
                    }
                }
            }
        }

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        void ISEAddInEdgeBarEventsEx.RemovePage(object theDocument)
        {
            // 去除线程
            var document = theDocument as SolidEdgeFramework.SolidEdgeDocument;
            RemoveEdgeBarPages(document);
        }

        void ISEAddInEdgeBarEventsEx.IsPageDisplayable(object theDocument, string EnvironmentCatID, out bool vbIsPageDisplayable)       
        {
            vbIsPageDisplayable = true;
        }




        #endregion SolidEdgeFramework.ISEAddInEdgeBarEventsEx

        #region RegAsm.exe callbacks
        /// <summary>
        /// Called when regasm.exe is executed against the assembly.
        /// </summary>
        [ComRegisterFunction]
        static void OnRegister(Type t)
        {
            try
            {
                var settings = new TGCommunity.AddIn.RegistrationSettings(t);

                settings.Enabled = true;
                settings.Environments.Add(TGSDK.EnvironmentCategories.Application);
                settings.Environments.Add(TGSDK.EnvironmentCategories.AllDocumentEnvrionments);

                // See http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx for LCID details.
                var englishCulture = CultureInfo.GetCultureInfo(1033);

                // Title & Summary are Locale specific. 
                settings.Titles.Add(englishCulture, "商飞铺层设计");
                settings.Summaries.Add(englishCulture, "用于商飞铺层设计验证");

                TGComacAddin.Register(settings);
            }
            catch (System.Exception ex)
            {
                System.Windows.MessageBox.Show(ex.StackTrace, ex.Message);
            }
        }

        /// <summary>
        /// Called when regasm.exe /u is executed against the assembly.
        /// </summary>
        [ComUnregisterFunction]
        static void OnUnregister(Type t)
        {
            TGComacAddin.Unregister(t);
        }

        void ISEDocumentEvents.BeforeClose()
        {
            
        }

        void ISEDocumentEvents.BeforeSave()
        {
            
        }

        void ISEDocumentEvents.AfterSave()
        {
            
        }

        void ISEDocumentEvents.SelectSetChanged(object SelectSet)
        {
            try
            {
                SolidEdgePart.PartDocument partDoc = _application.ActiveDocument as SolidEdgePart.PartDocument;
                if (null == partDoc)
                    return;

                // 遍历选择集
                SolidEdgeFramework.SelectSet selSet = SelectSet as SolidEdgeFramework.SelectSet;
                if (null == selSet)
                    return;

                // 收集要高亮的rossete坐标系

                HashSet<SolidEdgePart.Sketch3DFeature> rosetteList = new HashSet<SolidEdgePart.Sketch3DFeature>();
                int cnt = selSet.Count;
                for (int i = 0; i < cnt; i++)
                {
                    dynamic comObj =selSet.Item(i + 1);
                    dynamic typeId = comObj.Type;
                    SolidEdgePart.Sketch3DFeature sketch3d = comObj as SolidEdgePart.Sketch3DFeature;
                    if (sketch3d != null && sketch3d.Name.StartsWith("Rosette"))
                    {
                        rosetteList.Add(sketch3d);
                        continue;
                    }

                    try
                    {
                        sketch3d = comObj.Parent as SolidEdgePart.Sketch3DFeature;
                    }
                    catch(Exception)
                    {
                        sketch3d = null;   
                    }
                   
                    if (null == sketch3d)
                        continue;
                    if (sketch3d.Name.StartsWith("Rosette"))
                    {
                        rosetteList.Add(sketch3d);
                    }
                }

                // 高亮
                SolidEdgeFramework.HighlightSet hiset = null;
                if (partDoc.HighlightSets.Count > 0)
                {
                    hiset = partDoc.HighlightSets.Item(1l);
                }
                else
                {
                    hiset = partDoc.HighlightSets.Add();
                }
                hiset.Width = 4;
                // RGB(0,255,0)
                hiset.Color = 0x00FF00;
                hiset.RemoveAll();

                // 高显所有Rosette坐标系
                foreach (SolidEdgePart.Sketch3DFeature sketch3d in rosetteList)
                {
                    SolidEdgeGeometry.Edges line3ds = sketch3d.Edges[SolidEdgeGeometry.FeatureTopologyQueryTypeConstants.igQueryAll] as SolidEdgeGeometry.Edges;
                    cnt = line3ds.Count;
                    for (int i = 0; i < cnt; i++)
                    {
                        object edge  = line3ds.Item(i + 1);   
                        if (null == edge)
                            continue;
                        hiset.AddItem(edge);
                    }
                }

                hiset.Draw();
            }
            catch (System.Exception ex)
            {
                
            }
        }
        #endregion RegAsm.exe callbacks
    }
}